Load libraries
library(tidyverse)
library(googlesheets4)
library(fuzzyjoin)
Import google sheets of Billiken League data
#Pull pre-draft data
prefreeze_rosters <- read_sheet("https://docs.google.com/spreadsheets/d/1ZjlBTRAnW8vTzdr4rY-ciQWmYMPvEF5xtGQkUNLg4a0/edit?gid=0#gid=0", sheet = "PreFreezeRosters", col_types = 'ccccccc') %>%
filter(!is.na(player))
✔ Reading from Blue Socks 2025 Pre-Draft.
✔ Range ''PreFreezeRosters''.
#If you created a new google sheet, don't forget to change sharing permissions to "anyone with the link can edit" or you will get OAuth errors
frozen_rosters <- read_sheet("https://docs.google.com/spreadsheets/d/1ZjlBTRAnW8vTzdr4rY-ciQWmYMPvEF5xtGQkUNLg4a0/edit?gid=1871666303#gid=1871666303", sheet = "FrozenRosters", col_types = 'cccccc')
✔ Reading from Blue Socks 2025 Pre-Draft.
✔ Range ''FrozenRosters''.
draft <- read_sheet("https://docs.google.com/spreadsheets/d/1ZjlBTRAnW8vTzdr4rY-ciQWmYMPvEF5xtGQkUNLg4a0/edit?gid=1008004729#gid=1008004729", sheet = "Draft", col_types = 'ciiicccccc')
✔ Reading from Blue Socks 2025 Pre-Draft.
✔ Range ''Draft''.
New names:
salaries <- read_sheet("https://docs.google.com/spreadsheets/d/1ZjlBTRAnW8vTzdr4rY-ciQWmYMPvEF5xtGQkUNLg4a0/edit?gid=1123952567#gid=1123952567", sheet = "Salaries", col_types = 'ccc') %>%
rename(new_salary = Salary) %>%
filter(!is.na(Player)) %>%
mutate(across(c("new_salary"), ~gsub("\\$", "", .) %>% as.numeric))
✔ Reading from Blue Socks 2025 Pre-Draft.
✔ Range ''Salaries''.
positions <- suppressWarnings(read_sheet("https://docs.google.com/spreadsheets/d/1ZjlBTRAnW8vTzdr4rY-ciQWmYMPvEF5xtGQkUNLg4a0/edit?gid=605162437#gid=605162437", sheet = "Positions", col_types = 'ciiiiiiiiiii') %>%
mutate(PLAYER = gsub("\n.*","",PLAYER)) %>%
mutate(PLAYER = gsub("DTD.*","",PLAYER)) %>%
mutate(p_of = case_when(RF == 1 ~ 1, CF == 1 ~ 1, LF == 1 ~ 1, .default = 0)) %>%
mutate(p_ci = case_when(`1B` == 1 ~ 1, `3B` == 1 ~ 1, .default = 0)) %>%
mutate(p_mi = case_when(`2B` == 1 ~ 1, SS == 1 ~ 1, .default = 0)) %>%
rename(player = PLAYER, p_c = C, p_1b = `1B`, p_2b = `2B`, p_3b = `3B`, p_ss = SS, p_dh = DH) %>%
select(player, p_c, p_1b, p_2b, p_3b, p_ss, p_of, p_ci, p_mi, p_dh))
✔ Reading from Blue Socks 2025 Pre-Draft.
✔ Range ''Positions''.
Load FanGraphs Depth Charts Projections files
hitter_projections <- read_csv("hitter_projections_2025.csv") %>%
mutate(Name = stringi::stri_trans_general(Name, "Latin-ASCII"))
Rows: 618 Columns: 74── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (4): Name, Team, NameASCII, PlayerId
dbl (45): G, PA, AB, H, 1B, 2B, 3B, HR, R, RBI, BB, IBB, SO, HBP, SF, SH, SB, CS, AVG, BB%, K%, BB/K, OBP, SLG, wOBA, OP...
lgl (25): GDP, InterSD, InterSK, IntraSD, Vol, Skew, Dim, P10, P20, P30, P40, P50, P60, P70, P80, P90, TT10, TT20, TT30,...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
pitcher_projections <- read_csv("pitcher_projections_2025.csv") %>%
mutate(Name = stringi::stri_trans_general(Name, "Latin-ASCII"))
Rows: 837 Columns: 69── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (4): Name, Team, NameASCII, PlayerId
dbl (37): W, L, QS, ERA, G, GS, SV, HLD, IP, TBF, H, R, ER, HR, BB, HBP, SO, K/9, BB/9, K/BB, HR/9, K%, BB%, K-BB%, AVG,...
lgl (28): BS, IBB, GB%, HR/FB, InterSD, InterSK, IntraSD, Vol, Skew, Dim, P10, P20, P30, P40, P50, P60, P70, P80, P90, T...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
hitter_projections
pitcher_projections
NA
Filter out non-player rows
prefreeze_rosters <- prefreeze_rosters %>%
filter(!is.na(player)) %>%
mutate(across(c("salary"), ~gsub("\\$", "", .) %>% as.numeric))
Warning: There was 1 warning in `mutate()`.
ℹ In argument: `across(c("salary"), ~gsub("\\$", "", .) %>% as.numeric)`.
Caused by warning in `gsub("\\$", "", salary) %>% as.numeric`:
! NAs introduced by coercion
prefreeze_rosters
#filter(billikenTeam == "Blue Socks")
Merge projections with pre-freeze rosters
hitter_projections %>%
#Find NL projections only
filter(Team %in% c('ATL','LAD','SDP','ARI','NYM','PHI','MIL','STL','CHC','SFG','CIN','COL','PIT','MIA','WSN','NA')) %>%
#left_join(prefreeze_rosters, join_by('Name'=='player'))
stringdist_left_join(prefreeze_rosters, by = c("Name" = "player"), max_dist = 2)
pitcher_projections %>%
#Find NL projections only
filter(Team %in% c('ATL','LAD','SDP','ARI','NYM','PHI','MIL','STL','CHC','SFG','CIN','COL','PIT','MIA','WSN','NA')) %>%
#left_join(prefreeze_rosters, join_by('Name'=='player'))
stringdist_left_join(prefreeze_rosters, by = c("Name" = "player"), max_dist = 2)
NA
Team Totals
(hitter_team_totals <- hitter_projections %>%
#Find NL projections only
filter(Team %in% c('ATL','LAD','SDP','ARI','NYM','PHI','MIL','STL','CHC','SFG','CIN','COL','PIT','MIA','WSN','NA')) %>%
stringdist_left_join(prefreeze_rosters, by = c("Name" = "player"), max_dist = 2) %>%
group_by(billikenTeam) %>%
summarize(n=n(), PA = sum(PA), AB = sum(AB), H = sum(H), HR = sum(HR), R = sum(R), RBI = sum(RBI), SB = sum(SB), AVG = sum(H)/sum(AB)))
(pitcher_team_totals <- pitcher_projections %>%
#Find NL projections only
filter(Team %in% c('ATL','LAD','SDP','ARI','NYM','PHI','MIL','STL','CHC','SFG','CIN','COL','PIT','MIA','WSN','NA')) %>%
stringdist_left_join(prefreeze_rosters, by = c("Name" = "player"), max_dist = 2) %>%
group_by(billikenTeam) %>%
summarize(n=n(), W = sum(W), SV = sum(SV), IP = sum(IP), SO = sum(SO), ER = sum(ER), H = sum(H), BB = sum(BB), ERA = sum(ER)*9/sum(IP), WHIP = (sum(H)+sum(BB))/sum(IP)) )
NA
NA
Team Standings
#Rank Team Totals
n_teams <- pull(count(hitter_team_totals %>% filter(!is.na(billikenTeam)) %>% distinct(billikenTeam)))
hitter_points <- hitter_team_totals %>%
filter(!is.na(billikenTeam)) %>%
mutate(hr = n_teams+1 - dense_rank(desc(HR)), r = n_teams+1 - dense_rank(desc(R)), rbi = n_teams+1 - dense_rank(desc(RBI)), sb = n_teams+1 - dense_rank(desc(SB)), avg = n_teams+1 - dense_rank(desc(AVG))) %>%
mutate(hr_pct = (hr-1)/(n_teams-1), r_pct = (r-1)/(n_teams-1), rbi_pct = (rbi-1)/(n_teams-1), sb_pct = (sb-1)/(n_teams-1), avg_pct = (avg-1)/(n_teams-1)) %>%
mutate(hit = hr + r + rbi + sb + avg) %>%
arrange(desc(hit))
hitter_points
pitcher_points <- pitcher_team_totals %>%
filter(!is.na(billikenTeam)) %>%
mutate(w = n_teams+1 - dense_rank(desc(W)), sv = n_teams+1 - dense_rank(desc(SV)), so = n_teams+1 - dense_rank(desc(SO)), era = n_teams+1 - dense_rank(ERA), whip = n_teams+1 - dense_rank(WHIP)) %>%
mutate(w_pct = (w-1)/(n_teams-1), sv_pct = (sv-1)/(n_teams-1), so_pct = (so-1)/(n_teams-1), era_pct = (era-1)/(n_teams-1), whip_pct = (whip-1)/(n_teams-1)) %>%
mutate(pit = w + sv + so + era + whip) %>%
arrange(desc(pit))
pitcher_points
NA
Project category variability
ggplot(hitter_points, aes(HR, hr_pct)) +
geom_point() +
stat_smooth(method="glm", method.args = list(family=binomial))

ggplot(pitcher_points, aes(ERA, era_pct)) +
geom_point() +
stat_smooth(method="glm", method.args = list(family=binomial))

Fit logistic regression curves
w_model_glm <- glm(w_pct ~ W, data = pitcher_points, family = "binomial")
Warning: non-integer #successes in a binomial glm!
pitcher_points$w_pts_pred = predict(w_model_glm, pitcher_points, type="response")*8+1
sv_model_glm <- glm(sv_pct ~ SV, data = pitcher_points, family = "binomial")
Warning: non-integer #successes in a binomial glm!
pitcher_points$sv_pts_pred = predict(sv_model_glm, pitcher_points, type="response")*8+1
so_model_glm <- glm(so_pct ~ SO, data = pitcher_points, family = "binomial")
Warning: non-integer #successes in a binomial glm!
pitcher_points$so_pts_pred = predict(so_model_glm, pitcher_points, type="response")*8+1
era_model_glm <- glm(era_pct ~ ERA, data = pitcher_points, family = "binomial")
Warning: non-integer #successes in a binomial glm!
pitcher_points$era_pts_pred = predict(era_model_glm, pitcher_points, type="response")*8+1
whip_model_glm <- glm(whip_pct ~ WHIP, data = pitcher_points, family = "binomial")
Warning: non-integer #successes in a binomial glm!
pitcher_points$whip_pts_pred = predict(whip_model_glm, pitcher_points, type="response")*8+1
(pitcher_points <- pitcher_points %>%
mutate(pitcher_points_pred = w_pts_pred + sv_pts_pred + so_pts_pred + era_pts_pred + whip_pts_pred) %>%
arrange(desc(pitcher_points_pred)))
Player projected point impact (average, not situation-based)
Simple linear model by category
hr_model <- lm(hr ~ HR, hitter_points)
r_model <- lm(r ~ R, hitter_points)
rbi_model <- lm(rbi ~ RBI, hitter_points)
sb_model <- lm(sb ~ SB, hitter_points)
avg_model <- lm(avg ~ AVG, hitter_points)
w_model <- lm(w ~ W, pitcher_points)
sv_model <- lm(sv ~ SV, pitcher_points)
so_model <- lm(so ~ SO, pitcher_points)
era_model <- lm(era ~ ERA, pitcher_points)
whip_model <- lm(whip ~ WHIP, pitcher_points)
hr_factor = hr_model$coefficients["HR"]
r_factor = r_model$coefficients["R"]
rbi_factor = rbi_model$coefficients["RBI"]
sb_factor = sb_model$coefficients["SB"]
avg_factor = avg_model$coefficients["AVG"]
melonheads_h <- pull(hitter_team_totals %>% filter(billikenTeam == "Melonheads") %>% select(H))
melonheads_ab <- pull(hitter_team_totals %>% filter(billikenTeam == "Melonheads") %>% select(AB))
w_factor = w_model$coefficients["W"]
sv_factor = sv_model$coefficients["SV"]
so_factor = so_model$coefficients["SO"]
era_factor = era_model$coefficients["ERA"]
whip_factor = whip_model$coefficients["WHIP"]
melonheads_ip <- pull(pitcher_team_totals %>% filter(billikenTeam == "Melonheads") %>% select(IP))
melonheads_er <- pull(pitcher_team_totals %>% filter(billikenTeam == "Melonheads") %>% select(ER))
melonheads_wh <- pull(pitcher_team_totals %>% filter(billikenTeam == "Melonheads") %>% select(BB)) + pull(pitcher_team_totals %>% filter(billikenTeam == "Melonheads") %>% select(H))
Build list of project draft value
#List of available players
hitter_projections %>%
filter(Team %in% c('ATL','LAD','SDP','ARI','NYM','PHI','MIL','STL','CHC','SFG','CIN','COL','PIT','MIA','WSN','NA')) %>%
stringdist_left_join(prefreeze_rosters, by = c("Name" = "player"), max_dist = 2) %>%
filter(is.na(billikenTeam)) %>%
mutate(AVG = round(AVG,3)) %>%
mutate(point_value = round(HR * hr_factor + R * r_factor + RBI * rbi_factor + SB * sb_factor + avg_factor * ((melonheads_h + H)/(melonheads_ab + AB) - melonheads_h/melonheads_ab),1)) %>%
select(Name, Team, PA, HR, R, RBI, SB, AVG, point_value) %>%
arrange(desc(point_value))
NA
hitter_projections <- hitter_projections %>%
mutate(point_value = round(HR * hr_factor + R * r_factor + RBI * rbi_factor + SB * sb_factor + avg_factor * ((melonheads_h + H)/(melonheads_ab + AB) - melonheads_h/melonheads_ab),1))
pitcher_projections <- pitcher_projections %>%
mutate(point_value = round(W * w_factor + SV * sv_factor + SO * so_factor + era_factor * (9*(melonheads_er + ER)/(melonheads_ip + IP) - 9*melonheads_er/melonheads_ip) + whip_factor * ((melonheads_wh + BB + H)/(melonheads_ip + IP) - melonheads_wh/melonheads_ip),1))
bind_rows(hitter_projections, pitcher_projections) %>%
#filter(Team %in% c('ATL','LAD','SDP','ARI','NYM','PHI','MIL','STL','CHC','SFG','CIN','COL','PIT','MIA','WSN','NA')) %>%
stringdist_left_join(prefreeze_rosters, by = c("Name" = "player"), max_dist = 2) %>%
filter(billikenTeam == "Melonheads") %>%
mutate(AVG = round(AVG,3), ERA = round(ERA,2), WHIP = round(WHIP,2)) %>%
select(Name, Team, PA, HR, R, RBI, SB, AVG, IP, W, SV, SO, ERA, WHIP, point_value) %>%
arrange(desc(point_value))
NA
All projected players with billiken league details
projected_players <- bind_rows(hitter_projections, pitcher_projections) %>%
filter(Team %in% c('ATL','LAD','SDP','ARI','NYM','PHI','MIL','STL','CHC','SFG','CIN','COL','PIT','MIA','WSN','NA')) %>%
stringdist_left_join(prefreeze_rosters, by = c("Name" = "player"), max_dist = 2) %>%
stringdist_left_join(positions, by = c("Name" = "player"), max_dist = 2) %>%
#stringdist_left_join(salaries, by = c("Name" = "Player"), max_dist = 2) %>%
#mutate(salary = case_when(!is.na(billikenTeam) ~ salary, TRUE ~ new_salary)) %>%
#filter(is.na(Owner) & !is.na(billikenTeam)) %>%
mutate(AVG = round(AVG,3), ERA = round(ERA,2), WHIP = round(WHIP,2), SO = case_when(IP == 0 ~ NA, IP > 0 ~ SO)) %>%
mutate(HR = case_when(PA == 0 ~ NA, PA > 0 ~ HR), R = case_when(PA == 0 ~ NA, PA > 0 ~ R)) %>%
select(Name, billikenTeam, contract, salary, Team, PA, HR, R, RBI, SB, AVG, IP, W, SV, SO, ERA, WHIP, point_value, p_c, p_1b, p_2b, p_3b, p_ss, p_of, p_ci, p_mi, p_dh) %>%
arrange(desc(point_value)) #%>%
#filter(billikenTeam == "Melonheads")
projected_players
NA
Replacement level example plot
pos <- projected_players %>%
filter(p_c == 1) %>%
mutate(rank_c = row_number(desc(point_value)))
ggplot(pos, aes(x=rank_c, y=point_value)) +
geom_point() +
geom_vline(xintercept = 21, color = "red")

Calculate replacement level by position (with no shared
positions)
rl_c <- projected_players %>%
filter(p_c == 1) %>%
filter(row_number(desc(point_value)) == 21L) %>%
mutate(pos = 'c') %>%
select(Name, pos, point_value)
rl_1b <- projected_players %>%
filter(p_1b == 1) %>%
filter(row_number(desc(point_value)) == 16L) %>%
mutate(pos = '1b') %>%
select(Name, pos, point_value)
rl_2b <- projected_players %>%
filter(p_2b == 1) %>%
filter(row_number(desc(point_value)) == 16L) %>%
mutate(pos = '2b') %>%
select(Name, pos, point_value)
rl_3b <- projected_players %>%
filter(p_3b == 1) %>%
filter(row_number(desc(point_value)) == 16L) %>%
mutate(pos = '3b') %>%
select(Name, pos, point_value)
rl_ss <- projected_players %>%
filter(p_ss == 1) %>%
filter(row_number(desc(point_value)) == 16L) %>%
mutate(pos = 'ss') %>%
select(Name, pos, point_value)
rl_of <- projected_players %>%
filter(p_of == 1) %>%
filter(row_number(desc(point_value)) == 51L) %>%
mutate(pos = 'of') %>%
select(Name, pos, point_value)
rl_ci <- projected_players %>%
filter(p_ci == 1) %>%
filter(row_number(desc(point_value)) == 31L) %>%
mutate(pos = 'ci') %>%
select(Name, pos, point_value)
rl_mi <- projected_players %>%
filter(p_mi == 1) %>%
filter(row_number(desc(point_value)) == 31L) %>%
mutate(pos = 'mi') %>%
select(Name, pos, point_value)
rl_dh <- projected_players %>%
filter(p_dh == 1) %>%
filter(row_number(desc(point_value)) == 11L) %>%
mutate(pos = 'dh') %>%
select(Name, pos, point_value)
rl_util <- projected_players %>%
filter(row_number(desc(point_value)) == 151L) %>%
mutate(pos = 'util') %>%
select(Name, pos, point_value)
rl_p <- projected_players %>%
filter(IP > 0) %>%
filter(row_number(desc(point_value)) == 91L) %>%
mutate(pos = 'p') %>%
select(Name, pos, point_value)
(replacement_level <- rbind(rl_c, rl_1b, rl_2b, rl_3b, rl_ss, rl_of, rl_ci, rl_mi, rl_dh, rl_util, rl_p))
NA
Note - multiposition players not totally clean here.
Assume that we use the lowest replacement level of any position a
player qualifies for.
Points Above Replacement
par <- projected_players %>%
mutate(repl = case_when(IP > 0 ~ 1.7,
p_c == 1 ~ 1.9,
.default = 4.1)
) %>%
mutate(par = point_value - repl) %>%
arrange(desc(par)) %>%
select(Name, Team, billikenTeam, contract, salary, point_value, repl, par, PA, HR, R, RBI, SB, AVG, IP, W, SV, SO, ERA, WHIP, p_c, p_1b, p_2b, p_3b, p_ss, p_of, p_ci, p_mi, p_dh)
par
ggplot(par, aes(par,salary)) +
geom_point() +
geom_smooth(method="lm")

ev_model <- lm(salary ~ par, par)
#par$ev <- par$par*2.118+4.840
par$ev <- par$par*2.583+10.355
par$surplus <- par$ev - par$salary
Projections by Billiken Team
par %>%
#filter(is.na(Owner) & !is.na(billikenTeam)) %>%
#filter(IP>0) %>%
filter(billikenTeam == "Blue Socks") %>%
relocate(surplus, .after = par) %>%
arrange(desc(par))
#arrange(surplus)
Add in new salaries
Project/simulate draft - Project/simulate next draft pick
Factor in salaries and cap
Build form for draft picks with projected standings
par %>%
filter(is.na(billikenTeam)) %>%
relocate(surplus, .after = par) %>%
#filter(IP > 0 ) %>%
arrange(desc(par))
#arrange(desc(surplus))
LS0tCnRpdGxlOiAiSW1wb3J0IEJpbGxpa2VuIFNoZWV0cyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKTG9hZCBsaWJyYXJpZXMKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnb29nbGVzaGVldHM0KQpsaWJyYXJ5KGZ1enp5am9pbikKCmBgYAoKSW1wb3J0IGdvb2dsZSBzaGVldHMgb2YgQmlsbGlrZW4gTGVhZ3VlIGRhdGEKYGBge3J9CgojUHVsbCBwcmUtZHJhZnQgZGF0YQpwcmVmcmVlemVfcm9zdGVycyA8LSByZWFkX3NoZWV0KCJodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9zcHJlYWRzaGVldHMvZC8xWmpsQlRSQW5XOHZUemRyNHJZLWNpUVdtWU1QdkVGNXh0R1FrVU5MZzRhMC9lZGl0P2dpZD0wI2dpZD0wIiwgc2hlZXQgPSAiUHJlRnJlZXplUm9zdGVycyIsIGNvbF90eXBlcyA9ICdjY2NjY2NjJykgJT4lIAogIGZpbHRlcighaXMubmEocGxheWVyKSkKCiNJZiB5b3UgY3JlYXRlZCBhIG5ldyBnb29nbGUgc2hlZXQsIGRvbid0IGZvcmdldCB0byBjaGFuZ2Ugc2hhcmluZyBwZXJtaXNzaW9ucyB0byAiYW55b25lIHdpdGggdGhlIGxpbmsgY2FuIGVkaXQiIG9yIHlvdSB3aWxsIGdldCBPQXV0aCBlcnJvcnMKZnJvemVuX3Jvc3RlcnMgPC0gcmVhZF9zaGVldCgiaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vc3ByZWFkc2hlZXRzL2QvMVpqbEJUUkFuVzh2VHpkcjRyWS1jaVFXbVlNUHZFRjV4dEdRa1VOTGc0YTAvZWRpdD9naWQ9MTg3MTY2NjMwMyNnaWQ9MTg3MTY2NjMwMyIsIHNoZWV0ID0gIkZyb3plblJvc3RlcnMiLCBjb2xfdHlwZXMgPSAnY2NjY2NjJykKZHJhZnQgPC0gcmVhZF9zaGVldCgiaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vc3ByZWFkc2hlZXRzL2QvMVpqbEJUUkFuVzh2VHpkcjRyWS1jaVFXbVlNUHZFRjV4dEdRa1VOTGc0YTAvZWRpdD9naWQ9MTAwODAwNDcyOSNnaWQ9MTAwODAwNDcyOSIsIHNoZWV0ID0gIkRyYWZ0IiwgY29sX3R5cGVzID0gJ2NpaWljY2NjY2MnKQpzYWxhcmllcyA8LSByZWFkX3NoZWV0KCJodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9zcHJlYWRzaGVldHMvZC8xWmpsQlRSQW5XOHZUemRyNHJZLWNpUVdtWU1QdkVGNXh0R1FrVU5MZzRhMC9lZGl0P2dpZD0xMTIzOTUyNTY3I2dpZD0xMTIzOTUyNTY3Iiwgc2hlZXQgPSAiU2FsYXJpZXMiLCBjb2xfdHlwZXMgPSAnY2NjJykgJT4lIAogICAgcmVuYW1lKG5ld19zYWxhcnkgPSBTYWxhcnkpICAlPiUgCiAgICBmaWx0ZXIoIWlzLm5hKFBsYXllcikpICU+JSAgCiAgICBtdXRhdGUoYWNyb3NzKGMoIm5ld19zYWxhcnkiKSwgfmdzdWIoIlxcJCIsICIiLCAuKSAlPiUgYXMubnVtZXJpYykpCnBvc2l0aW9ucyA8LSBzdXBwcmVzc1dhcm5pbmdzKHJlYWRfc2hlZXQoImh0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzFaamxCVFJBblc4dlR6ZHI0clktY2lRV21ZTVB2RUY1eHRHUWtVTkxnNGEwL2VkaXQ/Z2lkPTYwNTE2MjQzNyNnaWQ9NjA1MTYyNDM3Iiwgc2hlZXQgPSAiUG9zaXRpb25zIiwgY29sX3R5cGVzID0gJ2NpaWlpaWlpaWlpaScpICU+JSAKICAgIG11dGF0ZShQTEFZRVIgPSBnc3ViKCJcbi4qIiwiIixQTEFZRVIpKSAlPiUgCiAgICBtdXRhdGUoUExBWUVSID0gZ3N1YigiRFRELioiLCIiLFBMQVlFUikpICU+JQogICAgbXV0YXRlKHBfb2YgPSBjYXNlX3doZW4oUkYgPT0gMSB+IDEsIENGID09IDEgfiAxLCBMRiA9PSAxIH4gMSwgLmRlZmF1bHQgPSAwKSkgJT4lCiAgICBtdXRhdGUocF9jaSA9IGNhc2Vfd2hlbihgMUJgID09IDEgfiAxLCBgM0JgID09IDEgfiAxLCAuZGVmYXVsdCA9IDApKSAlPiUKICAgIG11dGF0ZShwX21pID0gY2FzZV93aGVuKGAyQmAgPT0gMSB+IDEsIFNTID09IDEgfiAxLCAuZGVmYXVsdCA9IDApKSAlPiUgIAogICAgcmVuYW1lKHBsYXllciA9IFBMQVlFUiwgcF9jID0gQywgcF8xYiA9IGAxQmAsIHBfMmIgPSBgMkJgLCBwXzNiID0gYDNCYCwgcF9zcyA9IFNTLCBwX2RoID0gREgpICU+JSAKICAgIHNlbGVjdChwbGF5ZXIsIHBfYywgcF8xYiwgcF8yYiwgcF8zYiwgcF9zcywgcF9vZiwgcF9jaSwgcF9taSwgcF9kaCkpCgpgYGAKCkxvYWQgRmFuR3JhcGhzIERlcHRoIENoYXJ0cyBQcm9qZWN0aW9ucyBmaWxlcwpgYGB7cn0KaGl0dGVyX3Byb2plY3Rpb25zIDwtIHJlYWRfY3N2KCJoaXR0ZXJfcHJvamVjdGlvbnNfMjAyNS5jc3YiKSAlPiUgCiAgbXV0YXRlKE5hbWUgPSBzdHJpbmdpOjpzdHJpX3RyYW5zX2dlbmVyYWwoTmFtZSwgIkxhdGluLUFTQ0lJIikpCnBpdGNoZXJfcHJvamVjdGlvbnMgPC0gcmVhZF9jc3YoInBpdGNoZXJfcHJvamVjdGlvbnNfMjAyNS5jc3YiKSAlPiUgCiAgbXV0YXRlKE5hbWUgPSBzdHJpbmdpOjpzdHJpX3RyYW5zX2dlbmVyYWwoTmFtZSwgIkxhdGluLUFTQ0lJIikpCgpoaXR0ZXJfcHJvamVjdGlvbnMKcGl0Y2hlcl9wcm9qZWN0aW9ucwoKYGBgCgpGaWx0ZXIgb3V0IG5vbi1wbGF5ZXIgcm93cwpgYGB7cn0KcHJlZnJlZXplX3Jvc3RlcnMgPC0gcHJlZnJlZXplX3Jvc3RlcnMgJT4lIAogIGZpbHRlcighaXMubmEocGxheWVyKSkgJT4lIAogIG11dGF0ZShhY3Jvc3MoYygic2FsYXJ5IiksIH5nc3ViKCJcXCQiLCAiIiwgLikgJT4lIGFzLm51bWVyaWMpKQoKcHJlZnJlZXplX3Jvc3RlcnMKICAjZmlsdGVyKGJpbGxpa2VuVGVhbSA9PSAiQmx1ZSBTb2NrcyIpCgpgYGAKCgpNZXJnZSBwcm9qZWN0aW9ucyB3aXRoIHByZS1mcmVlemUgcm9zdGVycwoKYGBge3J9CmhpdHRlcl9wcm9qZWN0aW9ucyAlPiUgCiNGaW5kIE5MIHByb2plY3Rpb25zIG9ubHkKICBmaWx0ZXIoVGVhbSAlaW4lIGMoJ0FUTCcsJ0xBRCcsJ1NEUCcsJ0FSSScsJ05ZTScsJ1BISScsJ01JTCcsJ1NUTCcsJ0NIQycsJ1NGRycsJ0NJTicsJ0NPTCcsJ1BJVCcsJ01JQScsJ1dTTicsJ05BJykpICU+JQogICNsZWZ0X2pvaW4ocHJlZnJlZXplX3Jvc3RlcnMsIGpvaW5fYnkoJ05hbWUnPT0ncGxheWVyJykpIAogIHN0cmluZ2Rpc3RfbGVmdF9qb2luKHByZWZyZWV6ZV9yb3N0ZXJzLCBieSA9IGMoIk5hbWUiID0gInBsYXllciIpLCBtYXhfZGlzdCA9IDIpCgpwaXRjaGVyX3Byb2plY3Rpb25zICU+JSAKI0ZpbmQgTkwgcHJvamVjdGlvbnMgb25seQogIGZpbHRlcihUZWFtICVpbiUgYygnQVRMJywnTEFEJywnU0RQJywnQVJJJywnTllNJywnUEhJJywnTUlMJywnU1RMJywnQ0hDJywnU0ZHJywnQ0lOJywnQ09MJywnUElUJywnTUlBJywnV1NOJywnTkEnKSkgJT4lCiAgI2xlZnRfam9pbihwcmVmcmVlemVfcm9zdGVycywgam9pbl9ieSgnTmFtZSc9PSdwbGF5ZXInKSkgCiAgc3RyaW5nZGlzdF9sZWZ0X2pvaW4ocHJlZnJlZXplX3Jvc3RlcnMsIGJ5ID0gYygiTmFtZSIgPSAicGxheWVyIiksIG1heF9kaXN0ID0gMikKCmBgYAoKVGVhbSBUb3RhbHMKYGBge3J9CihoaXR0ZXJfdGVhbV90b3RhbHMgPC0gaGl0dGVyX3Byb2plY3Rpb25zICU+JSAKI0ZpbmQgTkwgcHJvamVjdGlvbnMgb25seQogIGZpbHRlcihUZWFtICVpbiUgYygnQVRMJywnTEFEJywnU0RQJywnQVJJJywnTllNJywnUEhJJywnTUlMJywnU1RMJywnQ0hDJywnU0ZHJywnQ0lOJywnQ09MJywnUElUJywnTUlBJywnV1NOJywnTkEnKSkgJT4lCiAgc3RyaW5nZGlzdF9sZWZ0X2pvaW4ocHJlZnJlZXplX3Jvc3RlcnMsIGJ5ID0gYygiTmFtZSIgPSAicGxheWVyIiksIG1heF9kaXN0ID0gMikgJT4lIAogIGdyb3VwX2J5KGJpbGxpa2VuVGVhbSkgJT4lIAogIHN1bW1hcml6ZShuPW4oKSwgUEEgPSBzdW0oUEEpLCBBQiA9IHN1bShBQiksIEggPSBzdW0oSCksIEhSID0gc3VtKEhSKSwgUiA9IHN1bShSKSwgUkJJID0gc3VtKFJCSSksIFNCID0gc3VtKFNCKSwgQVZHID0gc3VtKEgpL3N1bShBQikpKQoKCihwaXRjaGVyX3RlYW1fdG90YWxzIDwtIHBpdGNoZXJfcHJvamVjdGlvbnMgJT4lIAojRmluZCBOTCBwcm9qZWN0aW9ucyBvbmx5CiAgZmlsdGVyKFRlYW0gJWluJSBjKCdBVEwnLCdMQUQnLCdTRFAnLCdBUkknLCdOWU0nLCdQSEknLCdNSUwnLCdTVEwnLCdDSEMnLCdTRkcnLCdDSU4nLCdDT0wnLCdQSVQnLCdNSUEnLCdXU04nLCdOQScpKSAlPiUKICBzdHJpbmdkaXN0X2xlZnRfam9pbihwcmVmcmVlemVfcm9zdGVycywgYnkgPSBjKCJOYW1lIiA9ICJwbGF5ZXIiKSwgbWF4X2Rpc3QgPSAyKSAlPiUgCiAgZ3JvdXBfYnkoYmlsbGlrZW5UZWFtKSAlPiUgCiAgc3VtbWFyaXplKG49bigpLCBXID0gc3VtKFcpLCBTViA9IHN1bShTViksIElQID0gc3VtKElQKSwgU08gPSBzdW0oU08pLCBFUiA9IHN1bShFUiksIEggPSBzdW0oSCksIEJCID0gc3VtKEJCKSwgRVJBID0gc3VtKEVSKSo5L3N1bShJUCksIFdISVAgPSAoc3VtKEgpK3N1bShCQikpL3N1bShJUCkpICkKCgpgYGAKVGVhbSBTdGFuZGluZ3MKYGBge3J9CiNSYW5rIFRlYW0gVG90YWxzCm5fdGVhbXMgPC0gcHVsbChjb3VudChoaXR0ZXJfdGVhbV90b3RhbHMgJT4lIGZpbHRlcighaXMubmEoYmlsbGlrZW5UZWFtKSkgJT4lIGRpc3RpbmN0KGJpbGxpa2VuVGVhbSkpKQoKaGl0dGVyX3BvaW50cyA8LSBoaXR0ZXJfdGVhbV90b3RhbHMgJT4lIAogIGZpbHRlcighaXMubmEoYmlsbGlrZW5UZWFtKSkgJT4lIAogIG11dGF0ZShociA9IG5fdGVhbXMrMSAtIGRlbnNlX3JhbmsoZGVzYyhIUikpLCByID0gbl90ZWFtcysxIC0gZGVuc2VfcmFuayhkZXNjKFIpKSwgcmJpID0gbl90ZWFtcysxIC0gZGVuc2VfcmFuayhkZXNjKFJCSSkpLCBzYiA9IG5fdGVhbXMrMSAtIGRlbnNlX3JhbmsoZGVzYyhTQikpLCBhdmcgPSBuX3RlYW1zKzEgLSBkZW5zZV9yYW5rKGRlc2MoQVZHKSkpICU+JSAKICBtdXRhdGUoaHJfcGN0ID0gKGhyLTEpLyhuX3RlYW1zLTEpLCByX3BjdCA9IChyLTEpLyhuX3RlYW1zLTEpLCByYmlfcGN0ID0gKHJiaS0xKS8obl90ZWFtcy0xKSwgc2JfcGN0ID0gKHNiLTEpLyhuX3RlYW1zLTEpLCBhdmdfcGN0ID0gKGF2Zy0xKS8obl90ZWFtcy0xKSkgJT4lIAogIG11dGF0ZShoaXQgPSBociArIHIgKyByYmkgKyBzYiArIGF2ZykgJT4lIAogIGFycmFuZ2UoZGVzYyhoaXQpKQoKaGl0dGVyX3BvaW50cwoKcGl0Y2hlcl9wb2ludHMgPC0gcGl0Y2hlcl90ZWFtX3RvdGFscyAlPiUgCiAgZmlsdGVyKCFpcy5uYShiaWxsaWtlblRlYW0pKSAlPiUgCiAgbXV0YXRlKHcgPSBuX3RlYW1zKzEgLSBkZW5zZV9yYW5rKGRlc2MoVykpLCBzdiA9IG5fdGVhbXMrMSAtIGRlbnNlX3JhbmsoZGVzYyhTVikpLCBzbyA9IG5fdGVhbXMrMSAtIGRlbnNlX3JhbmsoZGVzYyhTTykpLCBlcmEgPSBuX3RlYW1zKzEgLSBkZW5zZV9yYW5rKEVSQSksIHdoaXAgPSBuX3RlYW1zKzEgLSBkZW5zZV9yYW5rKFdISVApKSAlPiUKICBtdXRhdGUod19wY3QgPSAody0xKS8obl90ZWFtcy0xKSwgc3ZfcGN0ID0gKHN2LTEpLyhuX3RlYW1zLTEpLCBzb19wY3QgPSAoc28tMSkvKG5fdGVhbXMtMSksIGVyYV9wY3QgPSAoZXJhLTEpLyhuX3RlYW1zLTEpLCB3aGlwX3BjdCA9ICh3aGlwLTEpLyhuX3RlYW1zLTEpKSAlPiUKICBtdXRhdGUocGl0ID0gdyArIHN2ICsgc28gKyBlcmEgKyB3aGlwKSAlPiUgCiAgYXJyYW5nZShkZXNjKHBpdCkpCgpwaXRjaGVyX3BvaW50cwoKYGBgCgoKUHJvamVjdCBjYXRlZ29yeSB2YXJpYWJpbGl0eQpgYGB7cn0KZ2dwbG90KGhpdHRlcl9wb2ludHMsIGFlcyhIUiwgaHJfcGN0KSkgKwogIGdlb21fcG9pbnQoKSArCiAgc3RhdF9zbW9vdGgobWV0aG9kPSJnbG0iLCBtZXRob2QuYXJncyA9IGxpc3QoZmFtaWx5PWJpbm9taWFsKSkKCmdncGxvdChwaXRjaGVyX3BvaW50cywgYWVzKEVSQSwgZXJhX3BjdCkpICsKICBnZW9tX3BvaW50KCkgKwogIHN0YXRfc21vb3RoKG1ldGhvZD0iZ2xtIiwgbWV0aG9kLmFyZ3MgPSBsaXN0KGZhbWlseT1iaW5vbWlhbCkpCgpgYGAKCkZpdCBsb2dpc3RpYyByZWdyZXNzaW9uIGN1cnZlcwpgYGB7cn0KaHJfbW9kZWxfZ2xtIDwtIGdsbShocl9wY3QgfiBIUiwgZGF0YSA9IGhpdHRlcl9wb2ludHMsIGZhbWlseSA9ICJiaW5vbWlhbCIpCmhpdHRlcl9wb2ludHMkaHJfcHRzX3ByZWQgPSBwcmVkaWN0KGhyX21vZGVsX2dsbSwgaGl0dGVyX3BvaW50cywgdHlwZT0icmVzcG9uc2UiKSo4KzEKCnJfbW9kZWxfZ2xtIDwtIGdsbShyX3BjdCB+IFIsIGRhdGEgPSBoaXR0ZXJfcG9pbnRzLCBmYW1pbHkgPSAiYmlub21pYWwiKQpoaXR0ZXJfcG9pbnRzJHJfcHRzX3ByZWQgPSBwcmVkaWN0KHJfbW9kZWxfZ2xtLCBoaXR0ZXJfcG9pbnRzLCB0eXBlPSJyZXNwb25zZSIpKjgrMQoKcmJpX21vZGVsX2dsbSA8LSBnbG0ocmJpX3BjdCB+IFJCSSwgZGF0YSA9IGhpdHRlcl9wb2ludHMsIGZhbWlseSA9ICJiaW5vbWlhbCIpCmhpdHRlcl9wb2ludHMkcmJpX3B0c19wcmVkID0gcHJlZGljdChyYmlfbW9kZWxfZ2xtLCBoaXR0ZXJfcG9pbnRzLCB0eXBlPSJyZXNwb25zZSIpKjgrMQoKc2JfbW9kZWxfZ2xtIDwtIGdsbShzYl9wY3QgfiBTQiwgZGF0YSA9IGhpdHRlcl9wb2ludHMsIGZhbWlseSA9ICJiaW5vbWlhbCIpCmhpdHRlcl9wb2ludHMkc2JfcHRzX3ByZWQgPSBwcmVkaWN0KHNiX21vZGVsX2dsbSwgaGl0dGVyX3BvaW50cywgdHlwZT0icmVzcG9uc2UiKSo4KzEKCmF2Z19tb2RlbF9nbG0gPC0gZ2xtKGF2Z19wY3QgfiBBVkcsIGRhdGEgPSBoaXR0ZXJfcG9pbnRzLCBmYW1pbHkgPSAiYmlub21pYWwiKQpoaXR0ZXJfcG9pbnRzJGF2Z19wdHNfcHJlZCA9IHByZWRpY3QoaHJfbW9kZWxfZ2xtLCBoaXR0ZXJfcG9pbnRzLCB0eXBlPSJyZXNwb25zZSIpKjgrMQoKKGhpdHRlcl9wb2ludHMgPC0gaGl0dGVyX3BvaW50cyAlPiUgCiAgbXV0YXRlKGhpdHRlcl9wb2ludHNfcHJlZCA9IGhyX3B0c19wcmVkICsgcl9wdHNfcHJlZCArIHJiaV9wdHNfcHJlZCArIHNiX3B0c19wcmVkICsgYXZnX3B0c19wcmVkKSAlPiUgCiAgICBhcnJhbmdlKGRlc2MoaGl0KSkpCgoKI2luc3RlYWQgb2YgcnVubmluZyB0aGlzIG9uIHRlYW1zLCBydW4gdGhpcyBmb3IgZXZlcnkgcG90ZW50aWFsIGRyYWZ0ZWQgcGxheWVyIHRvIHByb2plY3QgcG9pbnRzIGltcGFjdCBvZiBlYWNoIHBsYXllciBiYXNlZCBvbiB0aGUgY3VycmVudCBjb21wb3NpdGlvbiBvZiBlYWNoIHJvc3RlcgoKYGBgCgpgYGB7cn0KCndfbW9kZWxfZ2xtIDwtIGdsbSh3X3BjdCB+IFcsIGRhdGEgPSBwaXRjaGVyX3BvaW50cywgZmFtaWx5ID0gImJpbm9taWFsIikKcGl0Y2hlcl9wb2ludHMkd19wdHNfcHJlZCA9IHByZWRpY3Qod19tb2RlbF9nbG0sIHBpdGNoZXJfcG9pbnRzLCB0eXBlPSJyZXNwb25zZSIpKjgrMQoKc3ZfbW9kZWxfZ2xtIDwtIGdsbShzdl9wY3QgfiBTViwgZGF0YSA9IHBpdGNoZXJfcG9pbnRzLCBmYW1pbHkgPSAiYmlub21pYWwiKQpwaXRjaGVyX3BvaW50cyRzdl9wdHNfcHJlZCA9IHByZWRpY3Qoc3ZfbW9kZWxfZ2xtLCBwaXRjaGVyX3BvaW50cywgdHlwZT0icmVzcG9uc2UiKSo4KzEKCnNvX21vZGVsX2dsbSA8LSBnbG0oc29fcGN0IH4gU08sIGRhdGEgPSBwaXRjaGVyX3BvaW50cywgZmFtaWx5ID0gImJpbm9taWFsIikKcGl0Y2hlcl9wb2ludHMkc29fcHRzX3ByZWQgPSBwcmVkaWN0KHNvX21vZGVsX2dsbSwgcGl0Y2hlcl9wb2ludHMsIHR5cGU9InJlc3BvbnNlIikqOCsxCgplcmFfbW9kZWxfZ2xtIDwtIGdsbShlcmFfcGN0IH4gRVJBLCBkYXRhID0gcGl0Y2hlcl9wb2ludHMsIGZhbWlseSA9ICJiaW5vbWlhbCIpCnBpdGNoZXJfcG9pbnRzJGVyYV9wdHNfcHJlZCA9IHByZWRpY3QoZXJhX21vZGVsX2dsbSwgcGl0Y2hlcl9wb2ludHMsIHR5cGU9InJlc3BvbnNlIikqOCsxCgp3aGlwX21vZGVsX2dsbSA8LSBnbG0od2hpcF9wY3QgfiBXSElQLCBkYXRhID0gcGl0Y2hlcl9wb2ludHMsIGZhbWlseSA9ICJiaW5vbWlhbCIpCnBpdGNoZXJfcG9pbnRzJHdoaXBfcHRzX3ByZWQgPSBwcmVkaWN0KHdoaXBfbW9kZWxfZ2xtLCBwaXRjaGVyX3BvaW50cywgdHlwZT0icmVzcG9uc2UiKSo4KzEKCihwaXRjaGVyX3BvaW50cyA8LSBwaXRjaGVyX3BvaW50cyAlPiUgCiAgbXV0YXRlKHBpdGNoZXJfcG9pbnRzX3ByZWQgPSB3X3B0c19wcmVkICsgc3ZfcHRzX3ByZWQgKyBzb19wdHNfcHJlZCArIGVyYV9wdHNfcHJlZCArIHdoaXBfcHRzX3ByZWQpICU+JSAKICAgIGFycmFuZ2UoZGVzYyhwaXRjaGVyX3BvaW50c19wcmVkKSkpCgpgYGAKCmBgYHtyfQpoaXR0ZXJfcG9pbnRzICU+JSAKICBpbm5lcl9qb2luKHBpdGNoZXJfcG9pbnRzLCBieSA9IGpvaW5fYnkoYmlsbGlrZW5UZWFtKSkgJT4lIAogIG11dGF0ZSh0b3RhbCA9IHJvdW5kKGhpdHRlcl9wb2ludHNfcHJlZCArIHBpdGNoZXJfcG9pbnRzX3ByZWQsMSkpICU+JSAKICAjbXV0YXRlKHRvdGFsID0gaGl0ICsgcGl0KSAlPiUgCiAgc2VsZWN0KGJpbGxpa2VuVGVhbSwgdG90YWwsIGhyLCByLCByYmksIHNiLCBhdmcsIHcsIHN2LCBzbywgZXJhLCB3aGlwLCB0b3RhbCkgJT4lIAogIGFycmFuZ2UoZGVzYyh0b3RhbCkpIApgYGAKClBsYXllciBwcm9qZWN0ZWQgcG9pbnQgaW1wYWN0IChhdmVyYWdlLCBub3Qgc2l0dWF0aW9uLWJhc2VkKQoKU2ltcGxlIGxpbmVhciBtb2RlbCBieSBjYXRlZ29yeQpgYGB7cn0KaHJfbW9kZWwgPC0gbG0oaHIgfiBIUiwgaGl0dGVyX3BvaW50cykgCnJfbW9kZWwgPC0gbG0ociB+IFIsIGhpdHRlcl9wb2ludHMpIApyYmlfbW9kZWwgPC0gbG0ocmJpIH4gUkJJLCBoaXR0ZXJfcG9pbnRzKSAKc2JfbW9kZWwgPC0gbG0oc2IgfiBTQiwgaGl0dGVyX3BvaW50cykgCmF2Z19tb2RlbCA8LSBsbShhdmcgfiBBVkcsIGhpdHRlcl9wb2ludHMpIAoKd19tb2RlbCA8LSBsbSh3IH4gVywgcGl0Y2hlcl9wb2ludHMpIApzdl9tb2RlbCA8LSBsbShzdiB+IFNWLCBwaXRjaGVyX3BvaW50cykgCnNvX21vZGVsIDwtIGxtKHNvIH4gU08sIHBpdGNoZXJfcG9pbnRzKSAKZXJhX21vZGVsIDwtIGxtKGVyYSB+IEVSQSwgcGl0Y2hlcl9wb2ludHMpIAp3aGlwX21vZGVsIDwtIGxtKHdoaXAgfiBXSElQLCBwaXRjaGVyX3BvaW50cykgCgpocl9mYWN0b3IgPSBocl9tb2RlbCRjb2VmZmljaWVudHNbIkhSIl0Kcl9mYWN0b3IgPSByX21vZGVsJGNvZWZmaWNpZW50c1siUiJdCnJiaV9mYWN0b3IgPSByYmlfbW9kZWwkY29lZmZpY2llbnRzWyJSQkkiXQpzYl9mYWN0b3IgPSBzYl9tb2RlbCRjb2VmZmljaWVudHNbIlNCIl0KYXZnX2ZhY3RvciA9IGF2Z19tb2RlbCRjb2VmZmljaWVudHNbIkFWRyJdCgptZWxvbmhlYWRzX2ggPC0gcHVsbChoaXR0ZXJfdGVhbV90b3RhbHMgJT4lIGZpbHRlcihiaWxsaWtlblRlYW0gPT0gIk1lbG9uaGVhZHMiKSAlPiUgc2VsZWN0KEgpKQptZWxvbmhlYWRzX2FiIDwtIHB1bGwoaGl0dGVyX3RlYW1fdG90YWxzICU+JSBmaWx0ZXIoYmlsbGlrZW5UZWFtID09ICJNZWxvbmhlYWRzIikgJT4lIHNlbGVjdChBQikpCgp3X2ZhY3RvciA9IHdfbW9kZWwkY29lZmZpY2llbnRzWyJXIl0Kc3ZfZmFjdG9yID0gc3ZfbW9kZWwkY29lZmZpY2llbnRzWyJTViJdCnNvX2ZhY3RvciA9IHNvX21vZGVsJGNvZWZmaWNpZW50c1siU08iXQplcmFfZmFjdG9yID0gZXJhX21vZGVsJGNvZWZmaWNpZW50c1siRVJBIl0Kd2hpcF9mYWN0b3IgPSB3aGlwX21vZGVsJGNvZWZmaWNpZW50c1siV0hJUCJdCgptZWxvbmhlYWRzX2lwIDwtIHB1bGwocGl0Y2hlcl90ZWFtX3RvdGFscyAlPiUgZmlsdGVyKGJpbGxpa2VuVGVhbSA9PSAiTWVsb25oZWFkcyIpICU+JSBzZWxlY3QoSVApKQptZWxvbmhlYWRzX2VyIDwtIHB1bGwocGl0Y2hlcl90ZWFtX3RvdGFscyAlPiUgZmlsdGVyKGJpbGxpa2VuVGVhbSA9PSAiTWVsb25oZWFkcyIpICU+JSBzZWxlY3QoRVIpKQptZWxvbmhlYWRzX3doIDwtIHB1bGwocGl0Y2hlcl90ZWFtX3RvdGFscyAlPiUgZmlsdGVyKGJpbGxpa2VuVGVhbSA9PSAiTWVsb25oZWFkcyIpICU+JSBzZWxlY3QoQkIpKSArIHB1bGwocGl0Y2hlcl90ZWFtX3RvdGFscyAlPiUgZmlsdGVyKGJpbGxpa2VuVGVhbSA9PSAiTWVsb25oZWFkcyIpICU+JSBzZWxlY3QoSCkpCgpgYGAKCkJ1aWxkIGxpc3Qgb2YgcHJvamVjdCBkcmFmdCB2YWx1ZQpgYGB7cn0KI0xpc3Qgb2YgYXZhaWxhYmxlIHBsYXllcnMKaGl0dGVyX3Byb2plY3Rpb25zICU+JSAKICBmaWx0ZXIoVGVhbSAlaW4lIGMoJ0FUTCcsJ0xBRCcsJ1NEUCcsJ0FSSScsJ05ZTScsJ1BISScsJ01JTCcsJ1NUTCcsJ0NIQycsJ1NGRycsJ0NJTicsJ0NPTCcsJ1BJVCcsJ01JQScsJ1dTTicsJ05BJykpICU+JQogIHN0cmluZ2Rpc3RfbGVmdF9qb2luKHByZWZyZWV6ZV9yb3N0ZXJzLCBieSA9IGMoIk5hbWUiID0gInBsYXllciIpLCBtYXhfZGlzdCA9IDIpICU+JSAKICBmaWx0ZXIoaXMubmEoYmlsbGlrZW5UZWFtKSkgJT4lIAogIG11dGF0ZShBVkcgPSByb3VuZChBVkcsMykpICU+JSAKICBtdXRhdGUocG9pbnRfdmFsdWUgPSByb3VuZChIUiAqIGhyX2ZhY3RvciArIFIgKiByX2ZhY3RvciArIFJCSSAqIHJiaV9mYWN0b3IgKyBTQiAqIHNiX2ZhY3RvciArIGF2Z19mYWN0b3IgKiAoKG1lbG9uaGVhZHNfaCArIEgpLyhtZWxvbmhlYWRzX2FiICsgQUIpIC0gbWVsb25oZWFkc19oL21lbG9uaGVhZHNfYWIpLDEpKSAlPiUgIAogIHNlbGVjdChOYW1lLCBUZWFtLCBQQSwgSFIsIFIsIFJCSSwgU0IsIEFWRywgcG9pbnRfdmFsdWUpICU+JSAKICBhcnJhbmdlKGRlc2MocG9pbnRfdmFsdWUpKQoKYGBgCgpgYGB7cn0KaGl0dGVyX3Byb2plY3Rpb25zIDwtIGhpdHRlcl9wcm9qZWN0aW9ucyAlPiUgCiAgbXV0YXRlKHBvaW50X3ZhbHVlID0gcm91bmQoSFIgKiBocl9mYWN0b3IgKyBSICogcl9mYWN0b3IgKyBSQkkgKiByYmlfZmFjdG9yICsgU0IgKiBzYl9mYWN0b3IgKyBhdmdfZmFjdG9yICogKChtZWxvbmhlYWRzX2ggKyBIKS8obWVsb25oZWFkc19hYiArIEFCKSAtIG1lbG9uaGVhZHNfaC9tZWxvbmhlYWRzX2FiKSwxKSkKCnBpdGNoZXJfcHJvamVjdGlvbnMgPC0gcGl0Y2hlcl9wcm9qZWN0aW9ucyAlPiUgCiAgbXV0YXRlKHBvaW50X3ZhbHVlID0gcm91bmQoVyAqIHdfZmFjdG9yICsgU1YgKiBzdl9mYWN0b3IgKyBTTyAqIHNvX2ZhY3RvciArIGVyYV9mYWN0b3IgKiAoOSoobWVsb25oZWFkc19lciArIEVSKS8obWVsb25oZWFkc19pcCArIElQKSAtIDkqbWVsb25oZWFkc19lci9tZWxvbmhlYWRzX2lwKSArIHdoaXBfZmFjdG9yICogKChtZWxvbmhlYWRzX3doICsgQkIgKyBIKS8obWVsb25oZWFkc19pcCArIElQKSAtIG1lbG9uaGVhZHNfd2gvbWVsb25oZWFkc19pcCksMSkpCgpgYGAKCgpgYGB7cn0KYmluZF9yb3dzKGhpdHRlcl9wcm9qZWN0aW9ucywgcGl0Y2hlcl9wcm9qZWN0aW9ucykgJT4lIAogICNmaWx0ZXIoVGVhbSAlaW4lIGMoJ0FUTCcsJ0xBRCcsJ1NEUCcsJ0FSSScsJ05ZTScsJ1BISScsJ01JTCcsJ1NUTCcsJ0NIQycsJ1NGRycsJ0NJTicsJ0NPTCcsJ1BJVCcsJ01JQScsJ1dTTicsJ05BJykpICU+JQogIHN0cmluZ2Rpc3RfbGVmdF9qb2luKHByZWZyZWV6ZV9yb3N0ZXJzLCBieSA9IGMoIk5hbWUiID0gInBsYXllciIpLCBtYXhfZGlzdCA9IDIpICU+JSAKICBmaWx0ZXIoYmlsbGlrZW5UZWFtID09ICJNZWxvbmhlYWRzIikgJT4lIAogIG11dGF0ZShBVkcgPSByb3VuZChBVkcsMyksIEVSQSA9IHJvdW5kKEVSQSwyKSwgV0hJUCA9IHJvdW5kKFdISVAsMikpICU+JSAgCiAgc2VsZWN0KE5hbWUsIFRlYW0sIFBBLCBIUiwgUiwgUkJJLCBTQiwgQVZHLCBJUCwgVywgU1YsIFNPLCBFUkEsIFdISVAsIHBvaW50X3ZhbHVlKSAlPiUgCiAgYXJyYW5nZShkZXNjKHBvaW50X3ZhbHVlKSkKCmBgYAoKQWxsIHByb2plY3RlZCBwbGF5ZXJzIHdpdGggYmlsbGlrZW4gbGVhZ3VlIGRldGFpbHMKYGBge3J9CnByb2plY3RlZF9wbGF5ZXJzIDwtIGJpbmRfcm93cyhoaXR0ZXJfcHJvamVjdGlvbnMsIHBpdGNoZXJfcHJvamVjdGlvbnMpICU+JSAKICBmaWx0ZXIoVGVhbSAlaW4lIGMoJ0FUTCcsJ0xBRCcsJ1NEUCcsJ0FSSScsJ05ZTScsJ1BISScsJ01JTCcsJ1NUTCcsJ0NIQycsJ1NGRycsJ0NJTicsJ0NPTCcsJ1BJVCcsJ01JQScsJ1dTTicsJ05BJykpICU+JQogIHN0cmluZ2Rpc3RfbGVmdF9qb2luKHByZWZyZWV6ZV9yb3N0ZXJzLCBieSA9IGMoIk5hbWUiID0gInBsYXllciIpLCBtYXhfZGlzdCA9IDIpICU+JSAKICBzdHJpbmdkaXN0X2xlZnRfam9pbihwb3NpdGlvbnMsIGJ5ID0gYygiTmFtZSIgPSAicGxheWVyIiksIG1heF9kaXN0ID0gMikgJT4lIAogICNzdHJpbmdkaXN0X2xlZnRfam9pbihzYWxhcmllcywgYnkgPSBjKCJOYW1lIiA9ICJQbGF5ZXIiKSwgbWF4X2Rpc3QgPSAyKSAlPiUgCiAgI211dGF0ZShzYWxhcnkgPSBjYXNlX3doZW4oIWlzLm5hKGJpbGxpa2VuVGVhbSkgfiBzYWxhcnksIFRSVUUgfiBuZXdfc2FsYXJ5KSkgJT4lIAogICNmaWx0ZXIoaXMubmEoT3duZXIpICYgIWlzLm5hKGJpbGxpa2VuVGVhbSkpICU+JSAKICBtdXRhdGUoQVZHID0gcm91bmQoQVZHLDMpLCBFUkEgPSByb3VuZChFUkEsMiksIFdISVAgPSByb3VuZChXSElQLDIpLCBTTyA9IGNhc2Vfd2hlbihJUCA9PSAwIH4gTkEsIElQID4gMCB+IFNPKSkgJT4lICAKICBtdXRhdGUoSFIgPSBjYXNlX3doZW4oUEEgPT0gMCB+IE5BLCBQQSA+IDAgfiBIUiksIFIgPSBjYXNlX3doZW4oUEEgPT0gMCB+IE5BLCBQQSA+IDAgfiBSKSkgJT4lIAogIHNlbGVjdChOYW1lLCBiaWxsaWtlblRlYW0sIGNvbnRyYWN0LCBzYWxhcnksIFRlYW0sIFBBLCBIUiwgUiwgUkJJLCBTQiwgQVZHLCBJUCwgVywgU1YsIFNPLCBFUkEsIFdISVAsIHBvaW50X3ZhbHVlLCBwX2MsIHBfMWIsIHBfMmIsIHBfM2IsIHBfc3MsIHBfb2YsIHBfY2ksIHBfbWksIHBfZGgpICU+JSAKICBhcnJhbmdlKGRlc2MocG9pbnRfdmFsdWUpKSAjJT4lIAogICNmaWx0ZXIoYmlsbGlrZW5UZWFtID09ICJNZWxvbmhlYWRzIikgCgpwcm9qZWN0ZWRfcGxheWVycwoKYGBgCgpSZXBsYWNlbWVudCBsZXZlbCBleGFtcGxlIHBsb3QKYGBge3J9CnBvcyA8LSBwcm9qZWN0ZWRfcGxheWVycyAlPiUgCiAgZmlsdGVyKHBfYyA9PSAxKSAlPiUgCiAgbXV0YXRlKHJhbmtfYyA9IHJvd19udW1iZXIoZGVzYyhwb2ludF92YWx1ZSkpKQoKZ2dwbG90KHBvcywgYWVzKHg9cmFua19jLCB5PXBvaW50X3ZhbHVlKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMjEsIGNvbG9yID0gInJlZCIpCmBgYAoKCkNhbGN1bGF0ZSByZXBsYWNlbWVudCBsZXZlbCBieSBwb3NpdGlvbiAod2l0aCBubyBzaGFyZWQgcG9zaXRpb25zKQoKYGBge3J9CnJsX2MgPC0gcHJvamVjdGVkX3BsYXllcnMgJT4lIAogIGZpbHRlcihwX2MgPT0gMSkgJT4lIAogIGZpbHRlcihyb3dfbnVtYmVyKGRlc2MocG9pbnRfdmFsdWUpKSA9PSAyMUwpICU+JSAKICBtdXRhdGUocG9zID0gJ2MnKSAlPiUgCiAgc2VsZWN0KE5hbWUsIHBvcywgcG9pbnRfdmFsdWUpCgpybF8xYiA8LSBwcm9qZWN0ZWRfcGxheWVycyAlPiUgCiAgZmlsdGVyKHBfMWIgPT0gMSkgJT4lIAogIGZpbHRlcihyb3dfbnVtYmVyKGRlc2MocG9pbnRfdmFsdWUpKSA9PSAxNkwpICU+JSAKICBtdXRhdGUocG9zID0gJzFiJykgJT4lIAogIHNlbGVjdChOYW1lLCBwb3MsIHBvaW50X3ZhbHVlKQoKcmxfMmIgPC0gcHJvamVjdGVkX3BsYXllcnMgJT4lIAogIGZpbHRlcihwXzJiID09IDEpICU+JSAKICBmaWx0ZXIocm93X251bWJlcihkZXNjKHBvaW50X3ZhbHVlKSkgPT0gMTZMKSAlPiUgCiAgbXV0YXRlKHBvcyA9ICcyYicpICU+JSAKICBzZWxlY3QoTmFtZSwgcG9zLCBwb2ludF92YWx1ZSkKCnJsXzNiIDwtIHByb2plY3RlZF9wbGF5ZXJzICU+JSAKICBmaWx0ZXIocF8zYiA9PSAxKSAlPiUgCiAgZmlsdGVyKHJvd19udW1iZXIoZGVzYyhwb2ludF92YWx1ZSkpID09IDE2TCkgJT4lIAogIG11dGF0ZShwb3MgPSAnM2InKSAlPiUgCiAgc2VsZWN0KE5hbWUsIHBvcywgcG9pbnRfdmFsdWUpCgpybF9zcyA8LSBwcm9qZWN0ZWRfcGxheWVycyAlPiUgCiAgZmlsdGVyKHBfc3MgPT0gMSkgJT4lIAogIGZpbHRlcihyb3dfbnVtYmVyKGRlc2MocG9pbnRfdmFsdWUpKSA9PSAxNkwpICU+JSAKICBtdXRhdGUocG9zID0gJ3NzJykgJT4lIAogIHNlbGVjdChOYW1lLCBwb3MsIHBvaW50X3ZhbHVlKQoKcmxfb2YgPC0gcHJvamVjdGVkX3BsYXllcnMgJT4lIAogIGZpbHRlcihwX29mID09IDEpICU+JSAKICBmaWx0ZXIocm93X251bWJlcihkZXNjKHBvaW50X3ZhbHVlKSkgPT0gNTFMKSAlPiUgCiAgbXV0YXRlKHBvcyA9ICdvZicpICU+JSAKICBzZWxlY3QoTmFtZSwgcG9zLCBwb2ludF92YWx1ZSkKCnJsX2NpIDwtIHByb2plY3RlZF9wbGF5ZXJzICU+JSAKICBmaWx0ZXIocF9jaSA9PSAxKSAlPiUgCiAgZmlsdGVyKHJvd19udW1iZXIoZGVzYyhwb2ludF92YWx1ZSkpID09IDMxTCkgJT4lIAogIG11dGF0ZShwb3MgPSAnY2knKSAlPiUgCiAgc2VsZWN0KE5hbWUsIHBvcywgcG9pbnRfdmFsdWUpCgpybF9taSA8LSBwcm9qZWN0ZWRfcGxheWVycyAlPiUgCiAgZmlsdGVyKHBfbWkgPT0gMSkgJT4lIAogIGZpbHRlcihyb3dfbnVtYmVyKGRlc2MocG9pbnRfdmFsdWUpKSA9PSAzMUwpICU+JSAKICBtdXRhdGUocG9zID0gJ21pJykgJT4lIAogIHNlbGVjdChOYW1lLCBwb3MsIHBvaW50X3ZhbHVlKQoKcmxfZGggPC0gcHJvamVjdGVkX3BsYXllcnMgJT4lIAogIGZpbHRlcihwX2RoID09IDEpICU+JSAKICBmaWx0ZXIocm93X251bWJlcihkZXNjKHBvaW50X3ZhbHVlKSkgPT0gMTFMKSAlPiUgCiAgbXV0YXRlKHBvcyA9ICdkaCcpICU+JSAKICBzZWxlY3QoTmFtZSwgcG9zLCBwb2ludF92YWx1ZSkKCnJsX3V0aWwgPC0gcHJvamVjdGVkX3BsYXllcnMgJT4lIAogIGZpbHRlcihyb3dfbnVtYmVyKGRlc2MocG9pbnRfdmFsdWUpKSA9PSAxNTFMKSAlPiUgCiAgbXV0YXRlKHBvcyA9ICd1dGlsJykgJT4lIAogIHNlbGVjdChOYW1lLCBwb3MsIHBvaW50X3ZhbHVlKQoKcmxfcCA8LSBwcm9qZWN0ZWRfcGxheWVycyAlPiUgCiAgZmlsdGVyKElQID4gMCkgJT4lIAogIGZpbHRlcihyb3dfbnVtYmVyKGRlc2MocG9pbnRfdmFsdWUpKSA9PSA5MUwpICU+JSAKICBtdXRhdGUocG9zID0gJ3AnKSAlPiUgCiAgc2VsZWN0KE5hbWUsIHBvcywgcG9pbnRfdmFsdWUpCgoocmVwbGFjZW1lbnRfbGV2ZWwgPC0gcmJpbmQocmxfYywgcmxfMWIsIHJsXzJiLCBybF8zYiwgcmxfc3MsIHJsX29mLCBybF9jaSwgcmxfbWksIHJsX2RoLCBybF91dGlsLCBybF9wKSkKCmBgYApOb3RlIC0gbXVsdGlwb3NpdGlvbiBwbGF5ZXJzIG5vdCB0b3RhbGx5IGNsZWFuIGhlcmUuIAoKQXNzdW1lIHRoYXQgd2UgdXNlIHRoZSBsb3dlc3QgcmVwbGFjZW1lbnQgbGV2ZWwgb2YgYW55IHBvc2l0aW9uIGEgcGxheWVyIHF1YWxpZmllcyBmb3IuCgoKUG9pbnRzIEFib3ZlIFJlcGxhY2VtZW50CmBgYHtyfQpwYXIgPC0gcHJvamVjdGVkX3BsYXllcnMgJT4lIAogIG11dGF0ZShyZXBsID0gY2FzZV93aGVuKElQID4gMCB+IDEuNywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgcF9jID09IDEgfiAxLjksCiAgICAgICAgICAgICAgICAgICAgICAgICAgLmRlZmF1bHQgPSA0LjEpCiAgICAgICAgICkgJT4lIAogIG11dGF0ZShwYXIgPSBwb2ludF92YWx1ZSAtIHJlcGwpICU+JSAKICBhcnJhbmdlKGRlc2MocGFyKSkgJT4lIAogIHNlbGVjdChOYW1lLCBUZWFtLCBiaWxsaWtlblRlYW0sIGNvbnRyYWN0LCBzYWxhcnksIHBvaW50X3ZhbHVlLCByZXBsLCBwYXIsIFBBLCBIUiwgUiwgUkJJLCBTQiwgQVZHLCBJUCwgVywgU1YsIFNPLCBFUkEsIFdISVAsIHBfYywgcF8xYiwgcF8yYiwgcF8zYiwgcF9zcywgcF9vZiwgcF9jaSwgcF9taSwgcF9kaCkKCnBhcgpgYGAKCmBgYHtyfQpnZ3Bsb3QocGFyLCBhZXMocGFyLHNhbGFyeSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKQoKCmV2X21vZGVsIDwtIGxtKHNhbGFyeSB+IHBhciwgcGFyKSAKCgojcGFyJGV2IDwtIHBhciRwYXIqMi4xMTgrNC44NDAKcGFyJGV2IDwtIHBhciRwYXIqMi41ODMrMTAuMzU1CgpwYXIkc3VycGx1cyA8LSBwYXIkZXYgLSBwYXIkc2FsYXJ5CmBgYAoKClByb2plY3Rpb25zIGJ5IEJpbGxpa2VuIFRlYW0KYGBge3J9CnBhciAlPiUgCiAgI2ZpbHRlcihpcy5uYShPd25lcikgJiAhaXMubmEoYmlsbGlrZW5UZWFtKSkgJT4lIAogICNmaWx0ZXIoSVA+MCkgJT4lIAogIGZpbHRlcihiaWxsaWtlblRlYW0gPT0gIkJsdWUgU29ja3MiKSAlPiUgCiAgcmVsb2NhdGUoc3VycGx1cywgLmFmdGVyID0gcGFyKSAlPiUgCiAgYXJyYW5nZShkZXNjKHBhcikpIAogICNhcnJhbmdlKHN1cnBsdXMpIAoKYGBgCgpBZGQgaW4gbmV3IHNhbGFyaWVzCgpQcm9qZWN0L3NpbXVsYXRlIGRyYWZ0Ci0gUHJvamVjdC9zaW11bGF0ZSBuZXh0IGRyYWZ0IHBpY2sKCkZhY3RvciBpbiBzYWxhcmllcyBhbmQgY2FwCgpCdWlsZCBmb3JtIGZvciBkcmFmdCBwaWNrcyB3aXRoIHByb2plY3RlZCBzdGFuZGluZ3MKCmBgYHtyfQpwYXIgJT4lIAogIGZpbHRlcihpcy5uYShiaWxsaWtlblRlYW0pKSAlPiUgCiAgcmVsb2NhdGUoc3VycGx1cywgLmFmdGVyID0gcGFyKSAlPiUgCiAgI2ZpbHRlcihJUCA+IDAgKSAlPiUgCiAgYXJyYW5nZShkZXNjKHBhcikpIAogICNhcnJhbmdlKGRlc2Moc3VycGx1cykpIAoKYGBgCgo=